package models.weixin.auth;

import cache.CacheCallBack;
import cache.CacheHelper;
import helper.GlobalConfig;
import jodd.util.StringUtil;
import me.chanjar.weixin.common.bean.WxJsapiSignature;
import me.chanjar.weixin.common.exception.WxErrorException;
import me.chanjar.weixin.mp.api.WxMpConfigStorage;
import me.chanjar.weixin.mp.api.WxMpService;
import me.chanjar.weixin.mp.bean.result.WxMpOAuth2AccessToken;
import me.chanjar.weixin.mp.bean.result.WxMpUser;
import models.merchant.Merchant;
import models.merchant.VisitAuthUserMerchant;
import models.weixin.WebUser;
import models.weixin.WxMpHelper;
import org.apache.commons.lang.StringUtils;
import play.Logger;
import play.Play;
import play.mvc.After;
import play.mvc.Before;
import play.mvc.Controller;
import util.common.DateUtil;
import util.common.RandomNumberUtil;

import java.util.Arrays;
import java.util.Date;
import java.util.List;
import java.util.regex.Pattern;

/**
 * 微信公众号认证，基于OAuth.
 */
public class WxMpAuth extends Controller {


    private final static ThreadLocal<WebUser> _currentUser = new ThreadLocal<>();
    private final static ThreadLocal<Merchant> _currentMerchant = new ThreadLocal<>();
    private final static String ULC_CACHE_LINK_ID = "ULC_CACHE_LINKID_ID_";
    private final static String ULC_CACHE_WEB_USER = "ULC_CACHE_WEB_USER_";


    /**
     * 检查是否已经OAuth认证过，如果没有，则调用认证.
     * @throws Throwable
     */
    @Before(unless = {"login", "logout", "fail", "validate", "validation", "authenticate", "captcha"})
    public static void filter() throws Throwable {
        Logger.info("[Auth]: Filter for URL -> " +   request.host+ " |" +request.url);
        String linkId = params.get("linkId");
        renderArgs.put("host" , request.host);
        String uuid = params.get("uuid");
        if(StringUtil.isNotBlank(uuid)) {
            session.put("WEB_USER_UUID" , uuid);
        }
        Pattern pattern = Pattern.compile("[0-9]*");
        Logger.info("LOG 00010000 : LinkId : %s | Session.LinkId : %s" , linkId ,  session.get(GlobalConfig.WEIXIN_MP_WEIXIN_SESSION_LINK_ID));
//        session.put(GlobalConfig.WEIXIN_MP_WEIXIN_SESSION_LINK_ID , linkId);
        if(StringUtil.isBlank(linkId) || linkId.equals("undefined")) {
            linkId = session.get(GlobalConfig.WEIXIN_MP_WEIXIN_SESSION_LINK_ID);
            Logger.info("LOG 00010002 LinkId is %s" , linkId);
        } else {
//            Logger.info("LOG 00010003 LinkId is Not Null");
            if(pattern.matcher(linkId).matches() && !linkId.equals("favicon.ico")) {
//                Logger.info("LinkId : %s 是数字 " , linkId);
                session.put(GlobalConfig.WEIXIN_MP_WEIXIN_SESSION_LINK_ID , linkId);
            }
//            session.remove(GlobalConfig.WEIXIN_MP_WEIXIN_SESSION_USER_KEY);
            Logger.info("LOG 00010004 LinkId is %s" , linkId);
        }



//        linkId = linkId == null ? "1" : linkId;
        Logger.info("filter linkId :%s",linkId);
        String ua = "";
        if(request.headers.get("user-agent") != null) {
            ua = request.headers.get("user-agent").toString().toLowerCase();
        }
        Merchant merchant = Merchant.findByLinkIdUnCache(linkId);
        if (merchant == null) {
            Logger.info("找不到" + linkId + "对应的商户");
            renderText("不存在的应用地址，请检查域名是否正确.");
            return;
        }

        Logger.info("LOG Merchant.name : %s | merchant.weixinDate : %s" , merchant.name , merchant.weixinData.appId);
//        session.remove(GlobalConfig.WEIXIN_MP_WEIXIN_SESSION_USER_KEY + merchant.weixinData.id);
        renderArgs.put("linkId", linkId);
        _currentMerchant.set(merchant);
        renderArgs.put("currentMerchant", merchant);

//        Logger.info("用户使用浏览器 : %s" , ua);
        if (ua.indexOf("micromessenger") > 0) {// 是微信浏览器
            if (Play.mode.isProd()) {
                WxMpConfigStorage wxMpConfigStorage = WxMpHelper.getWxMpConfigStorage(merchant);
                Logger.info("LOG Storage  001 : %s" , wxMpConfigStorage.getAppId());
                if (wxMpConfigStorage == null) {
                    Logger.info("找不到" + linkId + "对应的CorpApp");
                    renderText("不存在的应用地址，请检查域名是否正确.");
                    return;
                }

                WxMpService wxMpService = WxMpHelper.getWxMpService(wxMpConfigStorage);
                Logger.info("LOG wxMpService  001 : %s" , wxMpService.getAccessToken());
//                Logger.info("filter Merchant : %s | linkId : %s", merchant, linkId);
                // 设置JSAPI
                UseJsApi annoUseJsAPI = getUseJsAPI();
                if (annoUseJsAPI != null) {
                    Logger.info("获取到的 代理的linkId : %s", linkId);
                    //                String jsapiTicket = wxCpService.getJsapiTicket(false);
                    String currentUrl = "http://" + request.host + request.url;
                    WxJsapiSignature jsSignature = wxMpService.createJsapiSignature(currentUrl);
                    //                Logger.info("url=" + currentUrl + ", jsapiTicket=" + jsapiTicket);
                    List<String> jsApis = Arrays.asList(annoUseJsAPI.value());
                    renderArgs.put("jsSignature", jsSignature);
                    Logger.info("jsSignature = " + jsSignature);
                    renderArgs.put("jsApis", jsApis);
                    Logger.info("jsApis = " + jsApis);
                    renderArgs.put("jsAppId", wxMpConfigStorage.getAppId());
                    Logger.info("jsAppId = " + wxMpConfigStorage.getAppId());
                }
                if (skipAuthCheck()) {
                    Logger.info("[Auth]: Skip the Auth Check.");
                    return;
                }
                //强行删掉缓存
//                session.remove(GlobalConfig.WEIXIN_MP_WEIXIN_SESSION_USER_KEY + merchant.weixinData.id);
                WebUser webUser = getUserFromSession(merchant);
                Logger.info("Secure.LoginUser : %s  ====== ", webUser);
                if (webUser == null) {
                    //是否有code&state参数？如果有，就是重定向回来的，否则就是进行重定向到微信OAuth地址.
                    String code = params.get("code");
                    String state = params.get("state");
                    Logger.info("   -----> code=%s, state=%s, accessToken=%s, session.get(WXMP_STATE)=%s",
                            code, state, wxMpService.getAccessToken(), session.get("WXMP_STATE"));
                    if (StringUtils.isBlank(code) || StringUtils.isBlank(state)) {
                        String targetUrl = "http://" + request.host + request.url;
                        String randomState = RandomNumberUtil.generateRandomChars(12);  // 考虑一下UUID
                        session.put("WXMP_STATE", randomState);
                        Logger.info("Log00125 session.WXMP_STATE: %s | targetUrl : %s", randomState, targetUrl);
                        Logger.info("LOG Storage  002 : %s" , wxMpConfigStorage.getAppId());
                        Logger.info("LOG wxMpService  002 : %s" , wxMpService.getAccessToken());
                        String redirectUrl = wxMpService.oauth2buildAuthorizationUrl(targetUrl, "snsapi_userinfo", randomState);
                        Logger.debug("LOG00126: [Auth]: oauth2.redirectUrl -> %s", redirectUrl);
                        redirect(redirectUrl);
                    } else {
                        WxMpOAuth2AccessToken wxMpOAuth2AccessToken = null;
                        try {
                            Logger.info("LOG Storage  003 : %s" , wxMpConfigStorage.getAppId());
                            Logger.info("LOG wxMpService  004 : %s" , wxMpService.getAccessToken());
                            wxMpOAuth2AccessToken = wxMpService.oauth2getAccessToken(code);
                            // 获取用户信息，并检查数据库中是否存在，如果不存在，就自动创建.
                            webUser = WxMpHelper.getUserFromOpenId(wxMpOAuth2AccessToken.getOpenId());
                            if (webUser == null || webUser.openId == null) {
                                Logger.info("Log00127 [Token] wxMpOAuth2AccessToken：%s", wxMpOAuth2AccessToken);
                                WxMpUser wxMpUser = new WxMpUser();
                                try{
                                    wxMpUser = wxMpService.oauth2getUserInfo(wxMpOAuth2AccessToken, "zh_CN");
                                } catch (Exception e) {
                                    wxMpUser.setOpenId(wxMpOAuth2AccessToken.getOpenId());
                                    wxMpUser.setNickname("临时用户");
                                }
                                Logger.info("Log00128 [WxMpUser] wxMpUser：%s", wxMpUser);
                                String dcode = params.get("dcode");
                                webUser = WxMpHelper.createOrUpdateUserFromWxMpUser(merchant, wxMpUser, dcode);
                                Logger.info("Log00129 [user] user：%s", webUser);
                            }
                        } catch (WxErrorException e) {
                            Logger.info("[Auth]: oauth2.state(%s) not equals session state (%s), bad request!!!", state, session.get("WXMP_STATE"));
                            String targetUrl = "http://" + request.host + request.url;
                            String randomState = RandomNumberUtil.generateRandomChars(12);  // 考虑一下UUID
                            session.put("WXMP_STATE", randomState);
                            Logger.info("Log00125_2 session.WXMP_STATE: %s", randomState);
                            String redirectUrl = wxMpService.oauth2buildAuthorizationUrl(targetUrl, "snsapi_userinfo", randomState);
                            Logger.debug("LOG00126_2: [Auth]: oauth2.redirectUrl -> %s", redirectUrl);
                            redirect(redirectUrl);
                        }
                    }
                }
                Logger.info("执行 WxMpAuth 结束 : %s ", DateUtil.dateToString(new Date(), "yyyy-MM-dd HH:mm:ss"));
                if (webUser != null) {

                    //保存  如果 UUID 存在
                    if(StringUtil.isNotBlank(uuid)) {
                        if(webUser.uuid == null || !webUser.uuid.equals(uuid)) {
                            webUser.uuid = uuid;
                            webUser.save();
                        }
                    }
                    session.put(GlobalConfig.WEIXIN_MP_WEIXIN_SESSION_USER_KEY + merchant.weixinData.id, webUser.id);
                    _currentUser.set(webUser);
                    renderArgs.put("currentUser", webUser);
                } else {
//                    Application.error("访问路径错误 请 ["+merchant.name+"] 进入");
                }
            }else {
                WebUser webUser = WebUser.all().first();
                if(webUser != null) {
                    session.put(GlobalConfig.WEIXIN_MP_WEIXIN_SESSION_USER_KEY + merchant.weixinData.id, webUser.id);
                    renderArgs.put("currentUser", webUser);
                    _currentUser.set(webUser);
                }
            }


        } else {
            //TODO 本地测试用
            WebUser webUser = WebUser.all().first();
            if(webUser != null) {
//                session.put(GlobalConfig.WEIXIN_MP_WEIXIN_SESSION_USER_KEY+ webUser.id, webUser.id);
                renderArgs.put("currentUser", webUser);
                _currentUser.set(webUser);
            }
//            Application.error("无法确定您的打开方式 请在微信内打开");
        }

        if(merchant.visitAuth != null && merchant.visitAuth) {
            WebUser webUser =  getUserFromSession(merchant);
            Long visitCount = VisitAuthUserMerchant.countByWebUserAndMerchant(webUser , merchant);
            if(visitCount == 0) {
//                renderText("未经授权 不能访问");
                redirect("/common/refuse/index");
            }
        }
    }


    @After
    public static void cleanCurrentUser() {
        _currentUser.remove();
    }

    /**
     * 在Controller中使用WexinCpAuth可以得到当前用户.
     */
    public static WebUser currentUser() {
        return _currentUser.get();
    }

    /**
     * 在Controller中使用WexinCpAuth可以得到当前用户.
     * 该用户为数据库用户. 从数据库仲更新数据
     */
    public static WebUser currentUpdateUser() {
        WebUser cacheUser = _currentUser.get();
        return WebUser.findById(cacheUser.id);
    }


    public static void logout(Merchant merchant) {
//        String sessionUserId = session.get(GlobalConfig.WEIXIN_MP_WEIXIN_SESSION_USER_KEY + merchant.weixinData.id);
        String sessionUserId = session.get(GlobalConfig.WEIXIN_MP_WEIXIN_SESSION_USER_KEY);
        if(StringUtils.isNotBlank(sessionUserId)) {
            CacheHelper.delete(WebUser.WXMP_CACHEKEY + sessionUserId);
        }
        session.clear();
    }

    /**
     * 尝试从Session中得到用户信息.
     */
    private static WebUser getUserFromSession(Merchant merchant) {
        if (Play.mode.isDev()) {
            WebUser webUser = WebUser.all().first();
            return webUser;
        } else {
            WebUser user = null;
            String sessionUserId = session.get(GlobalConfig.WEIXIN_MP_WEIXIN_SESSION_USER_KEY + merchant.weixinData.id);
//            String sessionUserId = session.get(GlobalConfig.WEIXIN_MP_WEIXIN_SESSION_USER_KEY);
            Logger.info("sessionUserId ： %s |  GlobalCOnfig : %s" , sessionUserId , GlobalConfig.WEIXIN_MP_WEIXIN_SESSION_USER_KEY + merchant.weixinData.id);

            if (StringUtils.isNotBlank(sessionUserId)) {

                if(sessionUserId.equals("117")) {
                    session.remove(GlobalConfig.WEIXIN_MP_WEIXIN_SESSION_USER_KEY + merchant.weixinData.id);
                    return null;
                }
                user = WebUser.findByUserId(Long.valueOf(sessionUserId));

//                final Long userId = Long.parseLong(sessionUserId);
//                user = CacheHelper.getCache(WebUser.WXMP_CACHEKEY + sessionUserId, new CacheCallBack<WebUser>() {
//                    @Override
//                    public WebUser loadData() {
//                        WebUser weixinUser = WebUser.findByUserId(userId);
//                        Logger.info("load user info=" + weixinUser.id);
//                        return weixinUser;
//                    }
//                });
                if (user == null) {
                    Logger.info("user is null or not actived user=" + user);
                    session.remove(GlobalConfig.WEIXIN_MP_WEIXIN_SESSION_USER_KEY + merchant.weixinData.id);
                    return null;
                }
            }

            if (user != null) {
//                session.put(GlobalConfig.WEIXIN_MP_WEIXIN_SESSION_USER_KEY + merchant.weixinData.id, user.id);
                session.put(GlobalConfig.WEIXIN_MP_WEIXIN_SESSION_USER_KEY + merchant.weixinData.id, user.id);
            }

            Logger.info("user=" + user);
            return user;
        }
    }

    public static Merchant currentMerchant() {
        return _currentMerchant.get();
    }

    public static Merchant currenUpdatetMerchant() {
        Merchant cacheMerchant =  _currentMerchant.get();
        if(cacheMerchant != null) {
            return Merchant.findById(cacheMerchant.id);
        }
        return  null;
    }

    /**
     * 检查是否要跳过认证检查.
     *
     * @return 返回true表明跳过检查.
     */
    private static boolean skipAuthCheck() {
        if (getActionAnnotation(SkipAuth.class) != null ||
                getControllerInheritedAnnotation(SkipAuth.class) != null) {
            Logger.info("skipAuthCheck=true");
            return true;
        }
        Logger.info("skipAuthCheck=false");
        return false;
    }

    /**
     * 检查是否要使用JSAPI.
     *
     * 在类名或方法上使用@UseJsAPI标注，会在renderArgs中加入一个jsSignature的对象.
     *
     * @return UseJsAPI，其value指定需要的权限.
     */
    private static UseJsApi getUseJsAPI() {
        if (getActionAnnotation(UseJsApi.class) != null) {
            Logger.info("UseJsAPI on Action");
            return getActionAnnotation(UseJsApi.class);
        }
        if (getControllerInheritedAnnotation(UseJsApi.class) != null) {
            Logger.info("UseJsAPI on Controller");
            return getControllerInheritedAnnotation(UseJsApi.class);
        }
        Logger.info("UseJsAPI=false");
        return null;
    }

}
